/*************************************************************
**    Filename  :
**
**    Author :  
**    Version :  
**    Date   :   
*************************************************************/

#include <sys/time.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

#include "osp.h"
#include "timer_api.h"
#include "dbgprint.h"

#define TASK_STACK_SIZE 16*1024
#define TIMER_EVENT  (0x00000001)

/*
 * The data structure which implements a timer.  
 */
static ostimer_t *_timerQ = NULL;
static U32 _timer_taskp;
//static U32 _timer_message;

S32 time_after(timer_clock_t Time1,timer_clock_t Time2)
{
    return ((Time1 > Time2) ? 1 : 0);
}

/*
 * Private function to remove a timer from the timer Q
 * The timer could be anywhere in the Q, or not present.
 *
 * Returns 1 if the head of the Q was deleted (the timer task
 * will need to be made aware of this).
 */
static S32 _timer_remove(ostimer_t * timerp)
{
    ostimer_t *prev = NULL;
    ostimer_t *this;
    S32 removed_head = 0;
    S32 removed = 0;
    /*
    * Enter critical section
    */
    task_lock();
    /*
    * Walk the Q, looking for the timer. If we find it,
    * remove it.
    */
    this = _timerQ;
    while (this && !removed)
    {
        if (this == timerp)
        {
            if (prev)
            {
                prev->next = this->next;
            }
            else
            {
                _timerQ = this->next;
                removed_head = 1;
            }
            removed = 1;
        }
        else
        {
            prev = this;
            this = this->next;
        }
    }
    /*
    * Exit critical section
    */
    task_unlock();
    /*
    * Return indicator if we deleted the head of the Q
    */
    return removed_head;
}

/*
 * Private function to add a timer to the ordered Q of
 * timers
 *
 * Returns 1 if the head of the Q was changed (the timer task
 * will need to be made aware of this).
 */
static S32 _timer_add(ostimer_t * timerp)
{
    ostimer_t *prev = NULL;
    ostimer_t *this;
    S32 located = 0;
    S32 added_head = 0;
    /*
    * Enter critical section
    */
    task_lock();
    /*
    * Walk the Q, looking for the correct place to insert it
    */
    this = _timerQ;
    timerp->first_time = timer_now();
    timerp->wake_time = timerp->first_time + timerp->period;
    while (this && !(located = time_after(this->wake_time, timerp->wake_time)))
    {
        prev = this;
        this = this->next;
    }
    /*
    * Insert the timer in the Q
    */
    if (prev)
    {
        prev->next = timerp;
    }
    else
    {
        _timerQ = timerp;
        added_head = 1;
    }
    timerp->next = this;
    timerp->queued = 1;
    /* 
    * Exit critical section
    */
    task_unlock();
    /*
    * Return indicator
    */
    return added_head;
}

/*
 * Timer task - responsible for performing the required
 * delays, and making the callbacks
 */
static void _timer_task(void *ptr)
{
    timer_clock_t wake_period = 0;
    ostimer_t *this = 0;
    ostimer_t *prev = 0;
    ostimer_t *calls = 0;
    //QUEUE_MESSAGE  _message;
    /*
    * Enter critical section
    */
    task_lock();
    /*
    * This task never terminates...
    */
    while (1)
    {
        /*
        * Work out which of the queued timers (if any) have expired
        */
        for (prev = 0, this = _timerQ; this && ((timer_now() >= this->wake_time) ); this = this->next)
        {
            prev = this;
            this->queued = 0;
        }
        /*
        * If any have expired, strip off them off from the global list,
        * and hold them locally.
        */
        if (prev)
        {
            calls = _timerQ;
            prev->next = 0;
            _timerQ = this;
        }
        else
        {
            calls = 0;
        }
        /*
        * Now make any callbacks that we've got (on the local list)
        * outside of the critical section.
        * Note that we grab the next ptr before we make the callback,
        * since the callback is at liberty to free the timer (provided its
        * not a periodic timer we're managing)!
        */

        task_unlock();
        while (calls)
        {
            ostimer_t *next = calls->next;
            calls->callback(calls,calls->FUNCPTR);    
            
            if (calls->flags == TIM_APP_MODE_REPEAT)
            {
                _timer_add(calls);
            }
            calls = next;
        }
        /*
        * Re-enter critical section
        */
        task_lock();
        /*
        * If there is anything on the list, then its expiry time
        * is how long we want to wait, otherwise wait until we
        * get told there is something
        */
        if (_timerQ)
        {
            wake_period = (_timerQ->wake_time - timer_now() );
            if(wake_period<0)
                wake_period = 1;
        }
        else
        {
            wake_period =(timer_clock_t)0;//TIMEOUT_INFINITY;
        }
        task_unlock();
//        if( wake_period>0 )
//            usleep(wake_period*1000);//OSPQueueGetMessage(_timer_message,&_message,TASK_WAIT,wake_period);
//        else if(_timerQ)
//            OSPQueueGetMessage(_timer_message,&_message,TASK_NOWAIT,0);
//        else
//            OSPQueueGetMessage(_timer_message,&_message,TASK_WAIT,0);
        // flush the message queue
//        while( SUCCESS == OSPQueueGetMessage(_timer_message,&_message,TASK_NOWAIT,0) )
//        ;
        usleep(10000);
        task_lock();
    }
}

/*
 * Exported entry point - initialises the library.
 * Creates the worker task and event group
 * Returns 0 for success, -1 for failure.
 */
unsigned int timer_initialize(void)
{
    unsigned int rc = -1;
    //OSPQueueCreate(0,32,WAIT_FIFO,&_timer_message);
    //if(_timer_message)
    {   
        OSPTaskCreate("Timer task", TASK_STACK_SIZE, _timer_task, OSP_HIGHEST_PRIORITY, 0, 0, (U32*)&_timer_taskp);
        if (_timer_taskp)
        {
            rc = 0;
        }
        else
        {
	     LOGE("Timer task Create faile!!!\n");
            //OSPQueueDelete(_timer_message);
        }
    }
    return rc;
}

/*
 * Exported entry point - create a timer
 */
ostimer_t *timer_create(void (*callback) (ostimer_t * id,TIM_VOID_FUNCTION_POINTER entry), timer_clock_t period, TIM_APP_MODE flags,TIM_VOID_FUNCTION_POINTER entry)
{
    ostimer_t *timerp = malloc(sizeof(ostimer_t));
    if (timerp)
    {
        memset( timerp, 0, sizeof(ostimer_t) );
        timerp->callback = callback;
        timerp->period = period;
        timerp->flags = flags;
        timerp->queued = 0;
        timerp->FUNCPTR = entry;
        timerp->first_time = 0;
    }
    return timerp;
}

/*
 * Exported entry point - arm a timer
 */
void timer_start(ostimer_t * timerp)
{
   // unsigned int ret = 0;
    //QUEUE_MESSAGE  _message;
    if (_timer_add(timerp))
    {
        //ret = OSPQueueSendMessage(_timer_message,&_message,OSP_MSGQ_NO_WAIT);
        //if( ret != OK )
            //LOGD( "Error sending timer message %d\n", __LINE__ );
    }
}

/*
 * Exported entry point - clear a timer
 */
void timer_stop(ostimer_t * timerp)
{
    //QUEUE_MESSAGE  _message;
    //S32 ret = 0;
    if (_timer_remove(timerp))
    {
        //ret = OSPQueueSendMessage(_timer_message,&_message,OSP_MSGQ_NO_WAIT);
        //if( ret != OK )
            //LOGD( "Error sending timer message %d\n", __LINE__ );
    }
}

/*
 * Exported entry point - delete a timer
 */
void timer_delete(ostimer_t * timerp)
{
    timer_stop(timerp);
    free(timerp);
}

/*
 * Return the period of a timer
 */
timer_clock_t timer_period(ostimer_t * timerp)
{
    return timerp->period;
}

/*
 * Change the period of a timer, and report
 * its old period
 */
timer_clock_t timer_period_set(ostimer_t * timerp, timer_clock_t period)
{
  //  S32 ret = 0;
    
    timer_clock_t old_period = timerp->period;
    //QUEUE_MESSAGE  _message;
    if (timerp->queued)
    {
        if (_timer_remove(timerp))
        {
            //ret = OSPQueueSendMessage(_timer_message,&_message,OSP_MSGQ_NO_WAIT);
            //if( ret != OK )
                //LOGD( "Error sending timer message %d\n", __LINE__ );
        }
        task_lock();
        timerp->first_time = timer_now();
        timerp->period = period;
        timerp->wake_time = timerp->first_time+period;
        task_unlock();
    }
    else
    {
        task_lock();
        timerp->first_time = timer_now();
        timerp->period = period;
        timerp->wake_time = timerp->first_time+period;
        task_unlock();
    }
    
    return old_period;
}
